package net.hn.hnms.biz.common.activiti.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import lombok.extern.slf4j.Slf4j;
import net.hn.hnms.biz.common.activiti.builder.UserTaskCreator;
import net.hn.hnms.biz.common.activiti.config.CustomProcessDiagramGenerator;
import net.hn.hnms.biz.common.activiti.config.Jump2TargetFlowNodeCommand;
import net.hn.hnms.biz.common.activiti.config.ProcessConstants;
import net.hn.hnms.biz.common.activiti.result.DeployResult;
import net.hn.hnms.biz.common.activiti.result.ProcessHistoryResult;
import net.hn.hnms.biz.common.activiti.result.ProcessTaskResult;
import net.hn.hnms.biz.common.activiti.result.ToDoTaskResult;
import net.hn.hnms.biz.common.activiti.service.ProcessApiService;
import net.hn.hnms.biz.common.activiti.utils.ProcessUtil;
import net.hn.hnms.biz.common.activiti.utils.ValidatorUtil;
import net.hn.hnms.sys.common.core.constant.Constants;
import net.hn.hnms.sys.common.core.exception.HuaNengException;
import net.hn.hnms.biz.common.activiti.params.*;
import net.hn.hnms.biz.common.activiti.params.*;
import org.activiti.bpmn.BpmnAutoLayout;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.*;
import org.activiti.engine.history.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ExecutionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created on 2022/8/5.
 *
 * @author ly
 * @desc: 流程处理api接口实现类
 */
@Slf4j
@Service
@DS("activiti")
public class ProcessApiServiceImpl implements ProcessApiService {

    @Resource
    private TaskService taskService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private HistoryService historyService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private ManagementService managementService;
    @Autowired
    private CustomProcessDiagramGenerator customProcessDiagramGenerator;

    /**
     * 启动工作流
     * @param processQueryParam 流程参数
     * @return 待执行的流程任务信息
     */
    @Transactional
    public ProcessTaskResult startProcess(ProcessQueryParam processQueryParam) {
        //手动参数校验
        if (StrUtil.isBlank(processQueryParam.getBusinessKey())) {
            throw new HuaNengException(Constants.FAIL,"业务编码不能为空");
        }
        if (StrUtil.isBlank(processQueryParam.getProcessName()) && StrUtil.isBlank(processQueryParam.getProcessDefinitionId())) {
            throw new HuaNengException(Constants.FAIL,"流程名称和流程定义ID必须有一个不能为空");
        }
        if (StrUtil.isBlank(processQueryParam.getApplyUser())) {
            throw new HuaNengException(Constants.FAIL,"申请人不能为空");
        }

        ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
        if (StrUtil.isNotBlank(processQueryParam.getProcessName())) {
            definitionQuery.processDefinitionName(processQueryParam.getProcessName());
        }
        if (StrUtil.isNotBlank(processQueryParam.getProcessDefinitionId())) {
            definitionQuery.processDefinitionId(processQueryParam.getProcessDefinitionId());
        }
        if (processQueryParam.getVersion() != null) {
            definitionQuery.processDefinitionVersion(processQueryParam.getVersion());
        }
        List<ProcessDefinition> definitionList = definitionQuery.active().orderByProcessDefinitionVersion().desc().list();
        if (CollUtil.isEmpty(definitionList)) {
            if (processQueryParam.getVersion() != null) {
                throw new HuaNengException(Constants.FAIL,"未找到已启用的流程[" + processQueryParam.getProcessName() + "]版本为[" + processQueryParam.getVersion() + "]的定义信息");
            }
            throw new HuaNengException(Constants.FAIL,"未找到已启用的流程[" + processQueryParam.getProcessName() + "]的定义信息");
        }
        ProcessDefinition definition = definitionList.get(0);

        if (CollUtil.isEmpty(processQueryParam.getVariables())) {
            processQueryParam.setVariables(new HashMap<>());
            processQueryParam.getVariables().put("executor", processQueryParam.getApplyUser());
        }

        try {
            // 1 幂等处理，判断此流程实例是否已经启动
            List<Task> listTask = getTasksByBusinessKey(processQueryParam.getBusinessKey());
            if (CollUtil.isNotEmpty(listTask)) {
                log.debug("业务数据：" + processQueryParam.getBusinessKey() + "的任务已存在.");
                throw new HuaNengException(Constants.FAIL,"该业务已存在审批中的数据,编号:" + processQueryParam.getBusinessKey());
            }
            //模拟springSecurity 授权
            Authentication.setAuthenticatedUserId(processQueryParam.getApplyUser());

            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(definition.getKey(), processQueryParam.getBusinessKey(), processQueryParam.getVariables());

            TaskParam taskParam = new TaskParam();
            taskParam.setExecutor(processQueryParam.getApplyUser());
            taskParam.setProcessInstanceId(processInstance.getId());
            return getTaskVariables(taskParam);
        } catch (Exception e) {
            throw new HuaNengException(Constants.FAIL,"启动失败：" + e.getMessage());
        }
    }

    /**
     * 获得流程任务执行参数
     * @param taskParam 流程任务参数
     * @return 待执行的流程任务信息
     */
    @Override
    public ProcessTaskResult getTaskVariables(TaskParam taskParam) {
        List<Task> taskList = queryForTask(taskParam);
        if (CollUtil.isEmpty(taskList)) {
            throw new HuaNengException(Constants.FAIL,"没有找到可执行的流程任务信息");
        }
        ProcessInstance instance = validateProcessInstance(taskList.get(0).getProcessInstanceId());

        ProcessTaskResult resp = new ProcessTaskResult();
        List<ToDoTaskResult> tasks = new ArrayList<>(taskList.size());
        for (Task task : taskList) {
            ToDoTaskResult taskResp = new ToDoTaskResult();
            //任务需要的参数
            String taskId = task.getId();
            Map<String, Object> variables = new HashMap<>();
            Map<String, Object> authMap = new HashMap<>();
            //查询当前流程 所需的发起人员和发起角色
            BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

            FlowNode currFlow = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

            //查询上一个流程 判断是否是起始流程
            boolean isStartEvent = isStartEvent(bpmnModel, currFlow);

            if (currFlow instanceof UserTask && !isStartEvent) {
                //节点用户
                List<String> users = ((UserTask) currFlow).getCandidateUsers();
                authMap.put("users", users);
                //节点角色
                List<String> roles = ((UserTask) currFlow).getCandidateGroups();
                authMap.put("roles", roles);
            }
            List<SequenceFlow> outgoingFlows = currFlow.getOutgoingFlows();
            for (SequenceFlow outgoingFlow : outgoingFlows) {
                variables.putAll(ProcessUtil.getCountersignParameters(outgoingFlow.getConditionExpression()));
                String targetNodeId = outgoingFlow.getTargetRef();
                FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(targetNodeId);
                if (targetFlowNode instanceof ExclusiveGateway) {
                    String conditionExpression = targetFlowNode.getOutgoingFlows().get(0).getConditionExpression();
                    variables.putAll(ProcessUtil.getCountersignParameters(conditionExpression));
                }
            }
            //如果不是起始流程 需要传入的参数默认都包含 是否通过 : approve  审核内容: comment
            if (!isStartEvent) {
                variables.put("approve", "通过:1;不通过:0;退回:2");
                variables.put("comment", "审核意见");
            }
            taskResp.setTaskId(taskId);
            taskResp.setBusinessKey(instance.getBusinessKey());
            taskResp.setTaskName(task.getName());
            taskResp.setVariables(variables);
            taskResp.setAuths(authMap);
            tasks.add(taskResp);
        }
        resp.setFinished(false);
        resp.setBusinessKey(instance.getBusinessKey());
        resp.setTasks(tasks);
        resp.setProcessInstanceId(taskList.get(0).getProcessInstanceId());
        return resp;
    }

    /**
     * 判断是不是起始节点
     *
     * @param bpmnModel
     * @param currFlow
     * @return
     */
    private boolean isStartEvent(BpmnModel bpmnModel, FlowNode currFlow) {
        //查询上一个流程 判断是否是起始流程
        FlowNode preFlow = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currFlow.getIncomingFlows().get(0).getSourceRef());

        return preFlow instanceof StartEvent;
    }

    /**
     * 验证流程状态
     *
     * @param processInstanceId
     */
    private ProcessInstance validateProcessInstance(String processInstanceId) {
        return validateProcessInstance(processInstanceId, false);
    }

    /**
     * 验证流程状态
     *
     * @param processInstanceId
     */
    private ProcessInstance validateProcessInstance(String processInstanceId, String businessKey) {
        return validateProcessInstance(processInstanceId, businessKey, false);
    }

    /**
     * 验证流程状态
     *
     * @param processInstanceId
     */
    private ProcessInstance validateProcessInstance(String processInstanceId, String businessKey, boolean activate) {
        if (StrUtil.isBlank(processInstanceId) && StrUtil.isBlank(businessKey)) {
            throw new HuaNengException(Constants.FAIL,"必须传入流程实例ID或者业务编号中的一个");
        }
        ProcessInstanceQuery instanceQuery = runtimeService.createProcessInstanceQuery();
        if (StrUtil.isNotBlank(processInstanceId)) {
            instanceQuery.processInstanceId(processInstanceId);
        }
        if (StrUtil.isNotBlank(businessKey)) {
            instanceQuery.processInstanceBusinessKey(businessKey);
        }
        ProcessInstance processInstance = instanceQuery.singleResult();
        if (processInstance == null) {
            throw new HuaNengException(Constants.FAIL,"流程已正常结束或没有该流程");
        }
        if (!activate && processInstance.isSuspended()) {
            throw new HuaNengException(Constants.FAIL,"流程已被暂停");
        }
        if (processInstance.isEnded()) {
            throw new HuaNengException(Constants.FAIL,"流程已正常结束");
        }
        return processInstance;
    }

    /**
     * 验证流程状态
     *
     * @param processInstanceId
     */
    private ProcessInstance validateProcessInstance(String processInstanceId, boolean activate) {
        return validateProcessInstance(processInstanceId, null, activate);
    }

    /**
     * 开始任务
     *
     * @param taskParam 流程任务参数
     * @return 待执行的流程任务信息
     */
    @Override
    @Transactional
    public ProcessTaskResult taskExecute(TaskParam taskParam) {
        if (StrUtil.isBlank(taskParam.getTaskId())) {
            throw new HuaNengException(Constants.FAIL,"流程任务id不能为空");
        }
        if (StrUtil.isBlank(taskParam.getExecutor())) {
            throw new HuaNengException(Constants.FAIL,"流程任务执行人不能为空");
        }
        Task task = taskService.createTaskQuery().taskId(taskParam.getTaskId()).orderByTaskCreateTime().desc().singleResult();
        if (task == null) {
            throw new HuaNengException(Constants.FAIL,"流程任务信息不存在");
        }
        //查询当前流程 所需的发起人员和发起角色

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        FlowNode currFlow = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
        //查询上一个流程 判断是否是起始流程
        boolean isStartEvent = isStartEvent(bpmnModel, currFlow);
        //执行完成任务
        taskParam.setVariables(taskParam.getVariables() == null ? new HashMap<>() : taskParam.getVariables());
        taskParam.getVariables().put("executor", taskParam.getExecutor());
        boolean finished = completeTask(taskParam.getTaskId(), taskParam.getVariables(), isStartEvent);
        if (!finished) {
            //未完成 返回 下一个任务需要的参数
            TaskParam nextTaskParam = new TaskParam();
            nextTaskParam.setProcessInstanceId(task.getProcessInstanceId());
            return getTaskVariables(nextTaskParam);
        } else {
            ProcessTaskResult resp = new ProcessTaskResult();
            resp.setFinished(true);
            resp.setProcessInstanceId(task.getProcessInstanceId());
            resp.setBusinessKey(task.getBusinessKey());
            resp.setTasks(ListUtil.empty());
            return resp;
        }
    }

    /**
     * 使用bpmn文件部署流程
     *
     * @param bpmn 流程文件
     * @param png 流程图片
     * @return 部署后返回信息
     */
    @Override
    @Transactional
    public DeployResult deployByBpmnFile(MultipartFile bpmn, MultipartFile png) {
        if (bpmn == null || bpmn.isEmpty()) {
            throw new HuaNengException(Constants.FAIL,"请上传流程文件");
        }
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        try {
            String bpmnName = bpmn.getOriginalFilename();
            deploymentBuilder.addInputStream(bpmnName, bpmn.getInputStream());
        } catch (IOException e) {
            log.error("流程文件部署失败,error->{0}",e);
            throw new HuaNengException(Constants.FAIL,"流程文件部署失败");
        }
        if (png != null && !png.isEmpty()) {
            try {
                String pngName = png.getOriginalFilename();
                deploymentBuilder.addInputStream(pngName, png.getInputStream());
            } catch (IOException e) {
                log.error("流程图片部署失败,error->{0}",e);
                throw new HuaNengException(Constants.FAIL,"流程图片部署失败");
            }
        }
        try {
            return doDeploy(deploymentBuilder.deploy());
        } catch (Exception e) {
            log.error("流程文件部署失败,error->{0}",e);
            if (e instanceof HuaNengException) {
                throw e;
            }
            throw new HuaNengException(Constants.FAIL,"流程文件部署失败");
        }
    }


    /**
     * 执行部署
     *
     * @param deployment
     * @return DeployResp
     */
    public DeployResult doDeploy(Deployment deployment) {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId()).singleResult();
        //流程定义key重复判断
        Set<String> oldNames = repositoryService.createProcessDefinitionQuery().processDefinitionKey(definition.getKey())
                .list().stream().map(ProcessDefinition::getName).collect(Collectors.toSet());
        if (oldNames.size() > 1) {
            throw new HuaNengException(Constants.FAIL,"流程编码[" + definition.getKey() + "]已在其他流程定义信息中被使用,请修改流程编后重新上传部署");
        }
        //流程定义名称重复判断
        Set<String> oldKeys = repositoryService.createProcessDefinitionQuery().processDefinitionName(definition.getName())
                .list().stream().map(ProcessDefinition::getKey).collect(Collectors.toSet());
        if (oldKeys.size() > 1) {
            throw new HuaNengException(Constants.FAIL,"流程名[" + definition.getName() + "]已在其他流程定义信息中被使用,请修改流程编后重新上传部署");
        }
        return definitionResult(definition);
    }

    /**
     * 手动创建流程
     *
     * @param processParam 流程创建参数
     * @return 部署后返回信息
     */
    @Transactional
    public DeployResult createWorkFlow(ProcessParam processParam) {
        ValidatorUtil.validateFast(processParam);
        if (CollUtil.isEmpty(processParam.getSteps()) || processParam.getSteps().size() < 2) {
            throw new HuaNengException(Constants.FAIL,"流程节点至少2个");
        }
        //部署工作流
        return deployWorkFlow(processParam, processParam.getSteps());
    }


    /**
     * 部署工作流
     *
     * @param workFlow 工作流信息
     * @param stepList 工作流节点
     */
    private DeployResult deployWorkFlow(ProcessParam workFlow, List<ProcessStepParam> stepList) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 1. 建立模型
        BpmnModel model = new BpmnModel();
        Process process = new Process();
        model.addProcess(process);
        process.setId("wf_" + DigestUtil.md5Hex(workFlow.getProcessName()));
        process.setName(workFlow.getProcessName());

        ValuedDataObject dataObject = new StringDataObject();
        dataObject.setName("creater");
        dataObject.setValue(workFlow.getCreator());
        process.setDataObjects(CollUtil.newArrayList(dataObject));
        process.setDocumentation(workFlow.getProcessContent());
        //添加流程
        //开始节点
        process.addFlowElement(ProcessUtil.createStartEvent());


        Map<String, RollBackNode> rollBackMap = new HashMap<>();
        for (int i = 0; i < stepList.size(); i++) {
            ProcessStepParam step = stepList.get(i);
            //判断是否会签
            if (step.getNodes().size() > 1) {
                //会签
                //加入并行网关-分支
                process.addFlowElement(ProcessUtil.createParallelGateway("parallelGateway-fork" + i, "并行网关-分支" + i));
                //获取角色下所有用户
                for (int u = 0; u < step.getNodes().size(); u++) {
                    ProcessNodeParam processNodeParam = step.getNodes().get(u);
                    //并行网关分支的审核节点
                    process.addFlowElement(new UserTaskCreator().id("userTask" + i + u)
                            .name(processNodeParam.getNodeName())
                            .requiredUsers(processNodeParam.getUsers())
                            .requiredRoles(processNodeParam.getRoles())
                            .build());
                }
                //并行网关-汇聚
                process.addFlowElement(ProcessUtil.createParallelGateway("parallelGateway-join" + i, "并行网关到-汇聚" + i));
            } else {
                ProcessNodeParam processNodeParam = step.getNodes().get(0);
                //普通流转
                //审核节点
                String taskId = "task" + i;
                process.addFlowElement(new UserTaskCreator().id("task" + i)
                        .name(processNodeParam.getNodeName())
                        .requiredUsers(processNodeParam.getUsers())
                        .requiredRoles(processNodeParam.getRoles())
                        .build());
                if (StrUtil.isNotBlank(processNodeParam.getRollBackNode())) {
                    UserTask userTask = (UserTask) process.getFlowElements().stream().filter(o -> StrUtil.equals(o.getName(), processNodeParam.getRollBackNode()))
                            .findFirst().orElse(null);
                    if (userTask == null) {
                        throw new HuaNengException(Constants.FAIL,"回退节点[" + processNodeParam.getRollBackNode() + "]不存在");
                    }
                    String repulseTaskId = "repulse" + i;
                    rollBackMap.put(taskId, new RollBackNode(userTask.getId(), repulseTaskId));
                    process.addFlowElement(new UserTaskCreator().id(repulseTaskId)
                            .name(processNodeParam.getNodeName() + "_回退节点")
                            .requiredUsers(CollUtil.newArrayList("${executor}"))
                            .build());
                }
            }
        }
        //结束节点
        process.addFlowElement(ProcessUtil.createEndEvent());
        //连线
        for (int y = 0; y < stepList.size(); y++) {
            ProcessStepParam step = stepList.get(y);
            List<ProcessNodeParam> nodes = step.getNodes();
            //是否会签
            if (nodes.size() > 1) {
                //会签
                //判断是否第一个节点
                if (y == 0) {
                    //开始节点和并行网关-分支连线
                    process.addFlowElement(ProcessUtil.createSequenceFlow("startEvent",
                            "parallelGateway-fork" + y, "开始节点->并行网关" + y, ""));
                } else {
                    //审核节点或者并行网关->并行网关
                    //判断上一个节点是否是会签
                    if (stepList.get(y - 1).getNodes().size() > 1) {
                        process.addFlowElement(ProcessUtil.createSequenceFlow("parallelGateway-join" + (y - 1),
                                "parallelGateway-fork" + y, "并行网关->并行网关" + y, ""));
                    } else {
                        process.addFlowElement(ProcessUtil.createSequenceFlow("task" + (y - 1),
                                "parallelGateway-fork" + y, "审核节点->并行网关" + y, ""));
                    }
                }
                //并行网关-分支和会签用户连线，会签用户和并行网关-汇聚连线
                for (int u = 0; u < step.getNodes().size(); u++) {
                    String taskId = "userTask" + y + u;
                    process.addFlowElement(ProcessUtil.createSequenceFlow("parallelGateway-fork" + y,
                            taskId, "并行网关->会节点签" + y + u, ""));
                    process.addFlowElement(ProcessUtil.createSequenceFlow(taskId,
                            "parallelGateway-join" + y, "会签节点->并行网关", ""));

                }
                //最后一个节点  并行网关->结束
                if (y == (stepList.size() - 1)) {
                    process.addFlowElement(ProcessUtil.createSequenceFlow("parallelGateway-join" + y,
                            "endEvent", "并行网关->结束节点", ""));
                }

            } else {
                String taskId = "task" + y;
                //普通流转
                //第一个节点
                if (y == 0) {
                    //开始节点和审核节点1
                    process.addFlowElement(ProcessUtil.createSequenceFlow("startEvent", taskId,
                            "开始节点->审核节点" + y, ""));
                } else {
                    //判断上一个节点是否会签
                    if (stepList.get(y - 1).getNodes().size() > 1) {
                        //会签
                        //并行网关->审核节点
                        process.addFlowElement(ProcessUtil.createSequenceFlow("parallelGateway-join" + (y - 1),
                                taskId, "并行网关->审核节点" + y, ""));
                    } else {
                        //普通
                        process.addFlowElement(ProcessUtil.createSequenceFlow("task" + (y - 1), taskId,
                                "审核节点" + (y - 1) + "到审核节点" + y, "${approve==1}"));
                    }
                }
                //是否最后一个节点
                if (y == (stepList.size() - 1)) {
                    //审核节点到结束节点
                    process.addFlowElement(ProcessUtil.createSequenceFlow(taskId, "endEvent",
                            "审核节点" + y + "到结束节点", "${approve==1}"));
                }
                RollBackNode rollBackNode = rollBackMap.get(taskId);
                if (rollBackNode != null) {
                    //审核节点到回退节点
                    process.addFlowElement(ProcessUtil.createSequenceFlow(taskId, rollBackNode.getRepulseNodeId(),
                            "审核退回" + y, "${approve==2}"));
                    process.addFlowElement(ProcessUtil.createSequenceFlow(rollBackNode.getRepulseNodeId(), rollBackNode.getTargetNodeId(),
                            "回退->审核节点" + y, ""));
                }
            }
        }

        // 2. 生成的图形信息
        new BpmnAutoLayout(model).execute();

        // 3. 部署流程
        Deployment deployment = processEngine.getRepositoryService().createDeployment()
                .addBpmnModel(process.getId() + ".bpmn", model).key(process.getId() + "_key").name(process.getId() + "_name").deploy();
        return doDeploy(deployment);
    }

    private DeployResult definitionResult(ProcessDefinition definition) {
        DeployResult resp = new DeployResult();
        resp.setProcessKey(definition.getKey());
        resp.setProcessName(definition.getName());
        resp.setVersion(definition.getVersion());
        resp.setDiagramResourceName(definition.getDiagramResourceName());
        resp.setDeploymentId(definition.getDeploymentId());
        resp.setProcessDefinitionId(definition.getId());
        resp.setResourceName(definition.getResourceName());
        resp.setSuspended(definition.isSuspended());
        return resp;
    }

    /**
     * 终止流程
     *
     * @param processDefinitionId
     * @param executionId
     * @param taskIds
     */
    private void endTask(String processDefinitionId, String executionId, List<String> taskIds) {
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        String endElementId = "";
        if (model != null) {
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for (FlowElement e : flowElements) {
                if (e instanceof EndEvent) {
                    endElementId = e.getId();
                    break;
                }
            }
        }
        historyService.createNativeHistoricTaskInstanceQuery().sql("DELETE FROM  act_hi_actinst WHERE " +
                        "proc_inst_id_=#{processInstanceId} AND task_id_=#{taskId}")
                .parameter("processInstanceId", processDefinitionId).parameter("taskId", taskIds.get(0)).singleResult();
        managementService.executeCommand(new Jump2TargetFlowNodeCommand(taskIds, endElementId, executionId));
    }


    /**
     * 根据参数查询流程任务
     *
     * @param taskParam
     * @return
     */
    private List<Task> queryForTask(TaskParam taskParam) {
        if (StrUtil.isBlank(taskParam.getTaskId()) && StrUtil.isBlank(taskParam.getProcessInstanceId()) && StrUtil.isBlank(taskParam.getBusinessKey())) {
            throw new HuaNengException(Constants.FAIL,"必须传入流程任务id或流程实例id或业务编号中的一个");
        }
        //使用businessKey查询
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        if (StrUtil.isNotBlank(taskParam.getBusinessKey())) {
            processInstanceQuery.processInstanceBusinessKey(taskParam.getBusinessKey());
            ProcessInstance instance = processInstanceQuery.singleResult();
            if (instance != null) {
                if (!StrUtil.equals(instance.getProcessInstanceId(), taskParam.getProcessInstanceId())) {
                    if (StrUtil.isBlank(taskParam.getProcessInstanceId())) {
                        taskParam.setProcessInstanceId(instance.getProcessInstanceId());
                    } else {
                        return ListUtil.empty();
                    }
                }
            } else {
                return ListUtil.empty();
            }
        }
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StrUtil.isNotBlank(taskParam.getTaskId())) {
            taskQuery.taskId(taskParam.getTaskId());
        }
        if (StrUtil.isNotBlank(taskParam.getProcessInstanceId())) {
            taskQuery.processInstanceId(taskParam.getProcessInstanceId());
        }
        return taskQuery.list();
    }

    /**
     * 撤销流程
     * @param processQueryParam 流程参数
     */
    @Transactional
    public void deleteProcess(ProcessQueryParam processQueryParam) {
        ProcessInstance instance = validateProcessInstance(processQueryParam.getProcessInstanceId(), processQueryParam.getBusinessKey());
        runtimeService.deleteProcessInstance(instance.getProcessInstanceId(), processQueryParam.getReason());
    }


    /**
     * 终止流程
     *
     * @param processQueryParam 流程参数
     */
    @Override
    public void endProcess(ProcessQueryParam processQueryParam) {
        ProcessInstance instance = validateProcessInstance(processQueryParam.getProcessInstanceId(), processQueryParam.getBusinessKey());
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(instance.getProcessInstanceId()).orderByTaskCreateTime().desc().list();
        if (CollUtil.isEmpty(tasks)) {
            throw new HuaNengException(Constants.FAIL,"流程任务信息不存在");
        }
        //终止流程
        endTask(tasks.get(0).getProcessDefinitionId(), tasks.get(0).getExecutionId(), tasks.stream().map(TaskInfo::getId).collect(Collectors.toList()));
    }


    /**
     * 暂停流程
     *
     * @param processQueryParam 流程参数
     */
    @Override
    public void suspendProcess(ProcessQueryParam processQueryParam) {
        ProcessInstance instance = validateProcessInstance(processQueryParam.getProcessInstanceId(), processQueryParam.getBusinessKey());
        runtimeService.suspendProcessInstanceById(instance.getProcessInstanceId());
    }

    /**
     * 激活流程
     *
     * @param processQueryParam 流程参数
     */
    @Override
    public void activateProcess(ProcessQueryParam processQueryParam) {
        ProcessInstance instance = validateProcessInstance(processQueryParam.getProcessInstanceId(), processQueryParam.getBusinessKey(), true);
        runtimeService.activateProcessInstanceById(instance.getProcessInstanceId());
    }

    /**
     * 根据 业务主键 获取任务实例
     */
    private List<Task> getTasksByBusinessKey(String businessKey) {
        return taskService.createTaskQuery().processInstanceBusinessKey(businessKey).list();
    }

    /**
     * 获取流程图像，已执行节点和流程线高亮显示
     *
     * @param processQueryParam 流程参数
     * @return InputStream 流程图像输入流
     */
    @Override
    public InputStream getActivityProcessImage(ProcessQueryParam processQueryParam) {
        String businessKey = processQueryParam.getBusinessKey();
        String processInstanceId = processQueryParam.getProcessInstanceId();
        if (StrUtil.isBlank(businessKey) && StrUtil.isBlank(processInstanceId)) {
            throw new HuaNengException(Constants.FAIL,"必须传入流程实例ID或业务编号中的一个");
        }
        log.info("[开始]-获取流程图图像");
        try {
            //  获取历史流程实例
            HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery();
            if (StrUtil.isNotBlank(businessKey)) {
                instanceQuery.processInstanceBusinessKey(businessKey);
            }
            if (StrUtil.isNotBlank(processInstanceId)) {
                instanceQuery.processInstanceId(processInstanceId);
            }
            HistoricProcessInstance historicProcessInstance = instanceQuery.singleResult();
            if (historicProcessInstance == null) {
                throw new HuaNengException(Constants.FAIL,"获取流程实例ID[" + processInstanceId + "]对应的历史流程实例失败！");
            } else {
                // 获取流程定义
                ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());

                // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
                List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().asc().list();
                // 已执行的节点ID集合
                List<String> executedActivityIdList = new ArrayList<String>();
                for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                    executedActivityIdList.add(activityInstance.getActivityId());
                }
                BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
                // 获取流程走过的线 (getHighLightedFlows是下面的方法)
                List<String> flowIds = getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);

                ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
                if (StrUtil.isNotBlank(businessKey)) {
                    executionQuery.processInstanceBusinessKey(businessKey);
                }
                if (StrUtil.isNotBlank(processInstanceId)) {
                    executionQuery.processInstanceId(processInstanceId);
                }
                Set<String> currIds = executionQuery
                        .list().stream()
                        .map(Execution::getActivityId).collect(Collectors.toSet());
                log.info("[完成]-获取流程图图像");
                return customProcessDiagramGenerator.generateDiagram(processInstanceId, bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "宋体", "宋体",
                        null, 1.0, new Color[]{ProcessConstants.COLOR_NORMAL, ProcessConstants.COLOR_CURRENT}, currIds);
            }
        } catch (Exception e) {
            log.error("【异常】-获取流程图失败！" + e.getMessage());
            throw new HuaNengException(Constants.FAIL,"获取流程图失败");
        }
    }

    private List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstanceList) {
        // 已流经的流程线，需要高亮显示
        List<String> highLightedFlowIdList = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstanceList = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            // 获取流程节点
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            allHistoricActivityNodeList.add(flowNode);
            // 结束时间不为空，当前节点则已经完成
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstanceList.add(historicActivityInstance);
            }
        }

        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        HistoricActivityInstance currentActivityInstance;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (int k = 0; k < finishedActivityInstanceList.size(); k++) {
            currentActivityInstance = finishedActivityInstanceList.get(k);
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            // 当前节点的所有流出线
            List<SequenceFlow> outgoingFlowList = currentFlowNode.getOutgoingFlows();

            /**
             * 遍历outgoingFlows并找到已流转的 满足如下条件认为已流转： 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转 (第2点有问题，有过驳回的，会只绘制驳回的流程线，通过走向下一级的流程线没有高亮显示)
             */
            if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow outgoingFlow : outgoingFlowList) {
                    // 获取当前节点流程线对应的下级节点
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef(), true);
                    // 如果下级节点包含在所有历史节点中，则将当前节点的流出线高亮显示
                    if (allHistoricActivityNodeList.contains(targetFlowNode)) {
                        highLightedFlowIdList.add(outgoingFlow.getId());
                    }
                }
            } else {
                /**
                 * 2、当前节点不是并行网关或兼容网关 【已解决-问题】如果当前节点有驳回功能，驳回到申请节点，则因为申请节点在历史节点中，导致当前节点驳回到申请节点的流程线被高亮显示，但实际并没有进行驳回操作
                 */
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                // 当前节点ID
                String currentActivityId = currentActivityInstance.getActivityId();
                int size = historicActivityInstanceList.size();
                boolean ifStartFind = false;
                boolean ifFinded = false;
                HistoricActivityInstance historicActivityInstance;
                // 循环当前节点的所有流出线
                // 循环所有历史节点
                log.info("【开始】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
                log.info("循环历史节点");
                for (int i = 0; i < historicActivityInstanceList.size(); i++) {
                    // 历史节点
                    historicActivityInstance = historicActivityInstanceList.get(i);
                    log.info("第【{}/{}】个历史节点-ActivityId=[{}]", i + 1, size, historicActivityInstance.getActivityId());
                    // 如果循环历史节点中的id等于当前节点id，从当前历史节点继续先后查找是否有当前节点流程线等于的节点
                    // 历史节点的序号需要大于等于已完成历史节点的序号，防止驳回重审一个节点经过两次是只取第一次的流出线高亮显示，第二次的不显示
                    if (i >= k && historicActivityInstance.getActivityId().equals(currentActivityId)) {
                        log.info("第[{}]个历史节点和当前节点一致-ActivityId=[{}]", i + 1, historicActivityInstance.getActivityId());
                        ifStartFind = true;
                        // 跳过当前节点继续查找下一个节点
                        continue;
                    }
                    if (ifStartFind) {
                        log.info("[开始]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);

                        ifFinded = false;
                        for (SequenceFlow sequenceFlow : outgoingFlowList) {
                            // 如果当前节点流程线对应的下级节点在其后面的历史节点中，则该条流程线进行高亮显示
                            // 【问题】
                            log.info("当前流出线的下级节点=[{}]", sequenceFlow.getTargetRef());
                            if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {

                                log.info("当前节点[{}]需高亮显示的流出线=[{}]", currentActivityId, sequenceFlow.getId());
                                highLightedFlowIdList.add(sequenceFlow.getId());
                                // 暂时默认找到离当前节点最近的下一级节点即退出循环，否则有多条流出线时将全部被高亮显示
                                ifFinded = true;
                                break;
                            }
                        }
                        log.info("[完成]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);
                    }
                    if (ifFinded) {
                        // 暂时默认找到离当前节点最近的下一级节点即退出历史节点循环，否则有多条流出线时将全部被高亮显示
                        break;
                    }
                }
                log.info("【完成】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
            }

        }
        return highLightedFlowIdList;

    }

    /**
     * 完成任务
     * @param taskId 流程任务id
     * @param variables 流程变量
     * @param isStartEvent 是否是起始事件
     * @return 流程是否处理完成标记
     */
    @Override
    @Transactional
    public Boolean completeTask(String taskId, Map<String, Object> variables, boolean isStartEvent) {
        log.debug("提交任务入参[taskId:" + taskId + ",variables:" + JSONUtil.toJsonStr(variables) + "]");
        final Task task = taskService.createTaskQuery().taskId(taskId).orderByTaskCreateTime().desc().singleResult();
        if (task == null) {
            throw new HuaNengException(Constants.FAIL,"流程任务信息不存在");
        }
        String executor = Convert.toStr(variables.get("executor"));

        if (isStartEvent) {
            //如果是起始流程 直接完成
            taskService.addComment(task.getId(), task.getProcessInstanceId(), "完成");
            try {
                taskService.claim(taskId, executor);
            } catch (Exception e) {
                throw new HuaNengException(Constants.FAIL,"当前流程任务的执行人不是" + executor);
            }
            variables.put("approve", 1);
            variables.put("comment", "系统默认审核");
            //完成用户申请任务
            taskService.setOwner(task.getId(), executor);
            taskService.complete(task.getId(), variables);
            return false;
        } else {
            //验证参数是否都有值
            verifyVariableIsFetch(taskId, variables);
            Integer approve = Convert.toInt(variables.get("approve"), -1);
            taskService.setVariable(taskId, "approve", approve == 0 ? 1 : approve);
            // 提交审核意见，审核意见放在variables中，key=comment
            String note = approve == 0 ? "【不同意】原因:" : (approve == 1 ? "【同意】:" : "【退回】原因:");
            taskService.addComment(task.getId(), task.getProcessInstanceId(), note + (StringUtils.isNotBlank((String) variables.get("comment")) ? (String) variables.get("comment") : (approve == 1 ? "" : "无")));
            taskService.setOwner(task.getId(), executor);
            taskService.claim(taskId, executor);
            taskService.complete(taskId, variables);

            List<Task> tasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).orderByTaskCreateTime().desc().list();

            //判断是否是最后一步
            boolean isFinished = tasks == null || tasks.size() == 0;
            if (isFinished) {
                return isFinished;
            }
            if (approve == 0) {
                //终止流程
                endTask(task.getProcessDefinitionId(), task.getExecutionId(), tasks.stream().map(TaskInfo::getId).collect(Collectors.toList()));
                isFinished = true;
            } else if (approve == 2) {
                //如果是回退节点
                List<Task> rollBackTasks = tasks.stream().filter(o -> StrUtil.contains(o.getName(), "_回退节点")).collect(Collectors.toList());
                //全部通过
                for (Task rollBackTask : rollBackTasks) {
                    TaskParam taskParam = new TaskParam();
                    taskParam.setTaskId(rollBackTask.getId());
                    taskParam.getVariables().put("approve", 1);
                    taskParam.getVariables().put("comment", "同意");
                    taskParam.setExecutor("系统");
                    this.taskExecute(taskParam);
                }
            }
            return isFinished;
        }

    }

    /**
     * 验证参数是否都匹配
     *
     * @param taskId
     */
    private void verifyVariableIsFetch(String taskId, Map<String, Object> variables) {
        TaskParam taskParam = new TaskParam();
        taskParam.setTaskId(taskId);
        ProcessTaskResult taskVariables = getTaskVariables(taskParam);
        if (CollUtil.isNotEmpty(taskVariables.getTasks())) {
            List<ToDoTaskResult> taskList = taskVariables.getTasks();
            ToDoTaskResult toDoTaskResult = taskList.stream().filter(o -> o.getTaskId().equals(taskId)).findAny().orElse(null);
            if (toDoTaskResult != null && CollUtil.isNotEmpty(toDoTaskResult.getVariables())) {
                Map<String, Object> requiredVars = toDoTaskResult.getVariables();
                if (!requiredVars.isEmpty()) {
                    Set<String> notExistsKeys = new HashSet<>();
                    requiredVars.forEach((key, value) -> {
                        if (!variables.containsKey(key)) {
                            notExistsKeys.add(key);
                        }
                    });
                    if (CollUtil.isNotEmpty(notExistsKeys)) {
                        throw new HuaNengException(Constants.FAIL,"流程任务处理参数缺少:[" + String.join(" ,", notExistsKeys) + "]");
                    }
                    if (variables.containsKey("approve")) {
                        int approve = Convert.toInt(variables.get("approve"), -1);
                        if (approve < 0 || approve > 2) {
                            throw new HuaNengException(Constants.FAIL,"流程任务参数approve必须是0,1,2,1:通过;0:不通过;2:退回");
                        }
                    }
                }
            }
        }
    }

    /**
     * 查询流程定义信息
     * @param processName 流程名称
     * @param version 版本
     * @param suspended 是否暂停
     * @return 流程部署信息集合
     */
    @Override
    public List<DeployResult> processDefinitionList(String processName, Integer version, Boolean suspended) {
        ProcessDefinitionQuery definitionQuery =
                repositoryService.createProcessDefinitionQuery();
        if (StrUtil.isNotBlank(processName)) {
            definitionQuery.processDefinitionNameLike(processName);
        }
        if (version != null) {
            definitionQuery.processDefinitionVersion(version);
        }
        if (suspended != null) {
            if (suspended) {
                definitionQuery.suspended();
            } else {
                definitionQuery.active();
            }
        }
        List<ProcessDefinition> processDefinitionEntities = definitionQuery
                .orderByProcessDefinitionName()
                .asc()
                .orderByProcessDefinitionVersion()
                .asc()
                .list();
        return processDefinitionEntities.stream().map(this::definitionResult).collect(Collectors.toList());
    }

    /**
     * 暂停/失效流程定义
     * @param processDefinitionId 流程定义记录id
     */
    @Override
    public void suspendProcessDefinition(String processDefinitionId) {
        validProcessDefinition(processDefinitionId, false);
        repositoryService.suspendProcessDefinitionById(processDefinitionId);
    }

    /**
     * 激活流程定义
     * @param processDefinitionId 流程定义记录id
     */
    @Override
    public void activateProcessDefinition(String processDefinitionId) {
        validProcessDefinition(processDefinitionId, true);
        repositoryService.activateProcessDefinitionById(processDefinitionId);
    }

    private void validProcessDefinition(String processDefinitionId, boolean activate) {
        if (StrUtil.isBlank(processDefinitionId)) {
            throw new HuaNengException(Constants.FAIL,"流程定义ID不能为空");
        }
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        if (definition == null) {
            throw new HuaNengException(Constants.FAIL,"流程定义信息不存在");
        }
        if (!activate && definition.isSuspended()) {
            throw new HuaNengException(Constants.FAIL,"流程定义已终止");
        }
        if (activate && !definition.isSuspended()) {
            throw new HuaNengException(Constants.FAIL,"流程定义正常,未终止");
        }
    }

    /**
     * 流程处理记录追踪 (processInstanceId 只能查询正在运行中的流程 businessKey 可以查询运行中或已结束的流程处理记录)
     * @param processQueryParam 流程参数
     * @return List<ProcessHistoryResult>
     */
    public List<ProcessHistoryResult> processTrackingList(ProcessQueryParam processQueryParam) {
        String processInstanceId = processQueryParam.getProcessInstanceId();
        String businessKey = processQueryParam.getBusinessKey();
        if (StrUtil.isBlank(processInstanceId) && StrUtil.isBlank(businessKey)) {
            throw new HuaNengException(Constants.FAIL,"必须传入流程实例ID或业务编号中的一个");
        }
        HistoricTaskInstanceQuery instanceQuery = historyService.createHistoricTaskInstanceQuery();
        if (StrUtil.isNotBlank(businessKey)) {
            instanceQuery.processInstanceBusinessKey(businessKey);
        }
        if (StrUtil.isNotBlank(processInstanceId)) {
            instanceQuery.processInstanceId(processInstanceId);
        }
        List<HistoricTaskInstance> list = instanceQuery
                .orderByHistoricTaskInstanceEndTime()
                .asc()
                .orderByHistoricTaskInstanceStartTime()
                .asc()
                .list();
        return list.stream().map(instance -> {
            ProcessHistoryResult result = new ProcessHistoryResult();
            List<Comment> comment = taskService.getTaskComments(instance.getId(), "comment");
            result.setComment(CollectionUtils.isEmpty(comment) ? "" : comment.get(0).getFullMessage());
            result.setTaskId(instance.getId());
            result.setProcessDefinitionId(instance.getProcessDefinitionId());
            result.setDeleteReason(instance.getDeleteReason());
            result.setParentTaskId(StrUtil.isBlank(instance.getParentTaskId()) ? "" : instance.getParentTaskId());
            result.setStartTime(instance.getStartTime());
            if (instance.getEndTime() != null) {
                result.setDuration(instance.getDurationInMillis());
                result.setEndTime(instance.getEndTime());
            }
            result.setActivityName(instance.getName());
            result.setExecutor(instance.getOwner());
            result.setResult(instance.getEndTime() != null ? "已完成" : "处理中");
            return result;
        }).collect(Collectors.toList());
    }
}
